/**
 * $Revision$
 * $Date$
 *
 * Copyright (C) https://gitee.com/baibaiclouds/platform loon. All rights reserved.
 * <p>
 * This software is the confidential and proprietary information of loon.
 * You shall not disclose such Confidential Information and shall use it only
 * in accordance with the terms of the agreements you entered into with loon.
 * 
 * Modified history:
 *   Loon  2019年11月15日 下午10:28:54  created
 */
package com.desktop.web.service.remotecpe;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.desktop.web.core.comenum.RemoteColorType;
import com.desktop.web.core.comenum.Status;
import com.desktop.web.core.utils.TaskEngine;
import com.desktop.web.core.utils.Util;
import com.desktop.web.service.device.DeviceService;
import com.desktop.web.uda.entity.Device;
import com.desktop.web.uda.entity.User;

/**
 * 
 * git https://github.com/fatedier/frp
 * 
 * @author baibai
 */
@Service
public class VirtulNatService implements InitializingBean {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${guac.depth.color}")
    private String GUAC_DEPTH_COLOR;

    @Value("${frp.last.date.max.sec}")
    private Integer LAST_DATE_MAX_SEC;

    @Autowired
    private DeviceService deviceService;

    private Map<String, NatInfo> uuidToNatInfo = new ConcurrentHashMap<>();
    private Map<String, String> snToUuid = new ConcurrentHashMap<>();

    @Override
    public void afterPropertiesSet() throws Exception {

        TaskEngine.getInstance().schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    timeoutNatInfo();
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }, 1000, 10 * 1000);
    }

    /**
     * 回收超时的nat信息
     */
    private void timeoutNatInfo() {
        Long curtime = System.currentTimeMillis();
        List<NatInfo> delList = new ArrayList<>();
        uuidToNatInfo.forEach((uuid, item) -> {
            long sec = curtime - item.getLastDate().getTime();
            sec = sec / 1000;
            if (sec > LAST_DATE_MAX_SEC) {
                delList.add(item);
            }
        });

        delList.forEach((item) -> {
            gcVirtualNatInfoByUUID(item.getUuid());
        });
    }

    /**
     * 获取可用端口
     * 
     * @param uid
     * @return
     */
    public NatInfo makeNatInfo(User user, Device device, Map<String, String> params) {

        NatInfo natInfo = new NatInfo();
        natInfo.setIsprober(Status.NO);
        natInfo.setTunnetType(device.getProtocol());
        natInfo.setUuid(Util.UUID16());
        natInfo.setUid(user.getId());
        natInfo.setRemotePort(device.getPort());
        natInfo.setCtime(new Date());
        natInfo.setLastDate(natInfo.getCtime());
        natInfo.setSelfname(user.getSelfname());
        natInfo.setUsername(user.getUsername());
        natInfo.setDeviceId(device.getId());
        natInfo.setDeviceSn(device.getSn());
        natInfo.setTargetId(user.getId());
        natInfo.setRemoteIp(device.getIp());
        natInfo.setUsername(device.getUsername());
        String password = deviceService.getPasswordById(device.getId());
        natInfo.setPassword(password);
        natInfo.setColor(RemoteColorType.valueOf(params.get("color")));

        snToUuid.put(device.getSn(), natInfo.getUuid());
        uuidToNatInfo.put(natInfo.getUuid(), natInfo);

        logger.info("make virtual natinfo success,uid:{},devcieid:{},uuid:{}", user.getId(), device.getId(), natInfo.getUuid());
        return natInfo;
    }

    /**
     * 根据uuid获取nat信息
     * 
     * @param uuid
     * @return
     */
    public NatInfo getNatInfoByUUID(String uuid) {
        if (StringUtils.isEmpty(uuid)) {
            return null;
        }

        NatInfo natInfo = uuidToNatInfo.get(uuid);
        if (natInfo == null) {
            return null;
        }

        return natInfo;
    }

    /**
     * 判断是否正在远程中
     * 
     * @param sn
     * @return
     */
    public boolean isRemoteingByDeviceSN(String sn) {
        return snToUuid.containsKey(sn);
    }

    /**
     * 根据sn获取nat信息
     * 
     * @param sn
     * @param uid
     * @return
     */
    public NatInfo getNatInfoByDeviceSN(String sn) {
        String uuid = snToUuid.get(sn);
        if (StringUtils.isEmpty(uuid)) {
            return null;
        }

        return this.getNatInfoByUUID(uuid);
    }

    /**
     * 保持心跳
     * 
     * @param uid
     * @param port
     */
    public void ping(String uuid) {
        NatInfo natInfo = this.getNatInfoByUUID(uuid);
        if (natInfo == null) {
            return;
        }

        natInfo.setLastDate(new Date());
        logger.debug("ping virtual success,uuid:{},sn:{}", uuid, natInfo.getDeviceSn());
    }

    /**
     * 根据uuid及时回收nat信息
     * 
     * @param uuid
     */
    public void gcVirtualNatInfoByUUID(String uuid) {
        NatInfo natInfo = this.getNatInfoByUUID(uuid);
        if (natInfo == null) {
            return;
        }

        uuidToNatInfo.remove(natInfo.getUuid());
        snToUuid.remove(natInfo.getDeviceSn());

        logger.debug("go virtual success,uuid:{},sn:{}", uuid, natInfo.getDeviceSn());
    }

    /**
     * 获取画质颜色
     * 
     * @return
     */
    public String getGuacDepthColor() {
        return GUAC_DEPTH_COLOR;
    }

    /**
     * 判断uuid是否存在
     * 
     * @param uuid
     * @return
     */
    public boolean existUUID(String uuid) {
        return uuidToNatInfo.containsKey(uuid);
    }

}
